package com.ynet.eq.aop.syslog;

import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.ynet.eq.aop.syslog.entity.Log;
import com.ynet.eq.aop.syslog.service.LogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;

/**
 * @author 胡超雄
 * @date 2021/7/15 11:53
 * @desc 日志切面处理
 */
@Aspect
@Component
@Slf4j
@Order(2)
public class SysLogAspect {


    @Resource
    private LogService logService;

    /**
     * 环绕通知
     *
     * @param point
     * @return Object
     * @throws Throwable
     */
    @Around("cutMethod()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        // 执行方法
        Object result = point.proceed();
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        // 保存日志
        try {
            //返回的参数
            String responseMsg = JSON.toJSONString(result);
            this.saveSysLog(point, responseMsg, time);
        } catch (Exception e) {
            log.error("获取日志失败", e);
        }
        return result;
    }

    /**
     * 异常处理
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "cutMethod()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        Object[] args = joinPoint.getArgs();
        String responseMsg = getErrMessage(e);
        this.saveSysLog(joinPoint, responseMsg, 0L);
    }


    private void saveSysLog(JoinPoint joinPoint, String responseMsg, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log sysLog = new Log();
        SysLog syslog = method.getAnnotation(SysLog.class);
        // 注解上的描述
        sysLog.setOperation(syslog.value());
        // 请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        sysLog.setMethod(className + "." + signature.getName() + "()");
        // 请求的参数
        Object[] args = joinPoint.getArgs();
        String params = "";
        try {
            params = JSON.toJSONString(args);
            sysLog.setParams(params);
        } catch (Exception ignored) {

        }

        // 获取request
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        String url = request.getRequestURL().toString();
        String method1 = request.getMethod();
        sysLog.setRequestPath(url);
        sysLog.setMethodName(method1);

        // 设置IP地址
        sysLog.setIp(ServletUtil.getClientIP(request));
        //String userName = SecurityUtils.getCurrentUsername();
        String userName = "";
        if (StringUtils.isBlank(userName)) {
            // 用户名
            sysLog.setUsername("UNKNOWN");
            //sysLog.setCreatedBy("UNKNOWN");
        } else {
            // 用户名
            sysLog.setUsername(userName);
            // sysLog.setCreatedBy(userName);
        }
        sysLog.setTime(time);
        sysLog.setRspParams(responseMsg);
        // sysLog.setCreatedAt(new Date());
        // 保存系统日志
        logService.save(sysLog);
        //log.info("用户: [{}] 请求: [{}] 参数: [{}] 请求耗时: [{}]", sysLog.getCreatedBy(), request.getRequestURI(), sysLog.getParams(), time);
    }


    /**
     * 定义切入点：对要拦截的方法进行定义与限制，如包、类
     * <p>
     * 1、execution(public * *(..)) 任意的公共方法
     * 2、execution（* set*（..）） 以set开头的所有的方法
     * 3、execution（* com.lingyejun.annotation.LoggerApply.*（..））com.lingyejun.annotation.LoggerApply这个类里的所有的方法
     * 4、execution（* com.lingyejun.annotation.*.*（..））com.lingyejun.annotation包下的所有的类的所有的方法
     * 5、execution（* com.lingyejun.annotation..*.*（..））com.lingyejun.annotation包及子包下所有的类的所有的方法
     * 6、execution(* com.lingyejun.annotation..*.*(String,?,Long)) com.lingyejun.annotation包及子包下所有的类的有三个参数，第一个参数为String类型，第二个参数为任意类型，第三个参数为Long类型的方法
     * 7、execution(@annotation(com.lingyejun.annotation.Lingyejun))
     */
    @Pointcut("@annotation(com.ynet.eq.aop.syslog.SysLog)")
    private void cutMethod() {

    }

    /**
     * 获取异常消息
     *
     * @param t
     * @return
     */
    public static String getErrMessage(Throwable t) {
        StringWriter stringWriter = new StringWriter();
        t.printStackTrace(new PrintWriter(stringWriter, true));
        return stringWriter.getBuffer().toString();
    }

}
